//===============================================================================
// Microsoft patterns & practices
// Guidance Explorer Offline Client
//===============================================================================
// Copyright  Microsoft Corporation.  All rights reserved.
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY
// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE.
//===============================================================================



using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;

namespace GuidanceExplorer.Model
{
    public enum ReadProtection { ReadWrite, ReadOnly };

    /// <summary>
    /// Represents a Library that has been loaded from a file.
    /// </summary>
    public class Library : FolderStructure
    {
        /// <summary>
        /// Factory.
        /// </summary>
        /// <returns></returns>
        public static Library New()
        {
            return new Library();
        }

        #region Private members

        /// <summary>
        /// The collection of guidance items.
        /// </summary>
        private GuidanceItemCollection _items = GuidanceItemCollection.New();

        /// <summary>
        /// The internal name of the library.
        /// </summary>
        private string _name;

        /// <summary>
        /// The caption of the library.
        /// </summary>
        private string _caption;

        /// <summary>
        /// Whether the library is read only or not.
        /// </summary>
        private ReadProtection _readProtection;

        /// <summary>
        /// Used to store read protection when the library has to be marked readonly - such as during a sync operation
        /// </summary>
        private ReadProtection _oldReadProtection;

        /// <summary>
        /// The name of the file that defines this library.
        /// </summary>
        private FileInfo _sourceFileName;

        /// <summary>
        /// Whether the library has been subscribed from a file.
        /// </summary>
        private bool _fileSubscribed;

        /// <summary>
        /// Whether the library has been subscribed from the web.
        /// </summary>
        private bool _webSubscribed;

        /// <summary>
        /// URL for web subscribed library location
        /// </summary>
        private string _url;

        /// <summary>
        /// The original source file where the libary was subscribed.
        /// </summary>
        private FileInfo _originalSourceFileInfo;

        /// <summary>
        /// Whether the library has changes
        /// </summary>
        private bool _hasChanges = false;

        /// <summary>
        /// Whether the library should be rendered as a file stream
        /// </summary>
        private bool _renderAsFile = true;

        #endregion

        /// <summary>
        /// Private default constructor.
        /// </summary>
        private Library() { }

        /// <summary>
        /// The internal name of the library.
        /// </summary>
        [Browsable(true)]
        [ReadOnly(true)]
        public string Name
        {
            get { return _name; }
            set { _name = value; }
        }

        /// <summary>
        /// Whether the library has changes
        /// </summary>
        [Browsable(false)]
        public bool HasChanges
        {
            get { return _hasChanges; }
            set { _hasChanges = value; }
        }

        /// <summary>
        /// Whether the library should be rendered as a file stream
        /// </summary>
        [Browsable(false)]
        public bool RenderAsFile
        {
            get { return _renderAsFile; }
            set { _renderAsFile = value; }
        }

        /// <summary>
        /// The caption of the library.
        /// </summary>
        [Browsable(true)]
        [ReadOnly(true)]
        public string Caption
        {
            get { return _caption; }
            set { _caption = value; }
        }

        /// <summary>
        /// Whether the library is read only or not.
        /// </summary>
        [Browsable(false)]
        public ReadProtection ReadProtection
        {
            get { return _readProtection; }
            set { _readProtection = value; }
        }

        /// <summary>
        /// Used to store read protection when the library has to be marked readonly - such as during a sync operation
        /// </summary>
        [Browsable(false)]
        public ReadProtection OldReadProtection
        {
            get { return _oldReadProtection; }
            set { _oldReadProtection = value; }
        }

        /// <summary>
        /// FolderStructures does not have items.
        /// </summary>
        [Browsable(false)]
        public override bool HasItems
        {
            get { return true; }
        }

        /// <summary>
        /// The items on this library.
        /// </summary>
        [Browsable(false)]
        public override GuidanceItemCollection Items
        {
            get { return _items; }
        }

        /// <summary>
        /// Whether the library has been subscribed.
        /// </summary>
        [Browsable(true)]
        [ReadOnly(true)]
        public bool FileSubscribed
        {
            get { return _fileSubscribed; }
            set { _fileSubscribed = value; }
        }

        /// <summary>
        /// Whether the library has been subscribed from the web.
        /// </summary>
        [Browsable(true)]
        [ReadOnly(true)]
        public bool WebSubscribed
        {
            get { return _webSubscribed; }
            set { _webSubscribed = value; }
        }

        /// <summary>
        /// Url for the web subscribed library
        /// </summary>
        [Browsable(true)]
        [ReadOnly(true)]
        public string URL
        {
            get { return _url; }
            set { _url = value; }
        }

        /// <summary>
        /// The name of the file that defines this library.
        /// </summary>
        [Browsable(true)]
        [ReadOnly(true)]
        public FileInfo SourceFileInfo
        {
            get { return _sourceFileName; }
            set { _sourceFileName = value; }
        }

        /// <summary>
        /// Get's the name of the index file for this library.
        /// </summary>
        [Browsable(true)]
        [ReadOnly(true)]
        public string IndexFileName
        {
            get
            {
                if (_fileSubscribed)
                {
                    return Path.Combine(Path.GetFullPath(Path.Combine(this.OriginalSourceFileInfo.DirectoryName, Caption)), "index.xml");
                }
                else
                {
                    return Path.Combine(Path.GetFullPath(Path.Combine(SourceFileInfo.DirectoryName, Caption)), "index.xml");
                }
            }
        }
        
        /// <summary>
        /// The original source file where the libary was subscribed.
        /// </summary>
        public FileInfo OriginalSourceFileInfo
        {
            get { return _originalSourceFileInfo; }
            set { _originalSourceFileInfo = value; }
        }

        public override IEnumerator<GuidanceItem> GetEnumerator()
        {
            foreach (GuidanceItem item in _items.Values)
            {
                yield return item;
            }
        }

        /// <summary>
        /// Count of items in this library for this guidance type provided.
        /// </summary>
        /// <param name="guidanceType"></param>
        /// <returns></returns>
        public int GuidanceItemCountInLibrary(GuidanceType guidanceType)
        {
            int count = 0;
            //TODO: cache the result per guidance type.
            foreach (GuidanceItem item in this.Items.Values)
            {
                GuidanceItemFileReference fileRef = item as GuidanceItemFileReference;
                if (fileRef != null)
                {
                    if (fileRef.GuidanceType.Name == guidanceType.Name)
                    {
                        count++;
                    }
                }
            }
            return count;
        }

        /// <summary>
        /// Count of items in this library for all the guidance types that are contained in the provided label
        /// </summary>
        /// <param name="guidanceType"></param>
        /// <returns></returns>
        public int GuidanceItemCountInLibrary(string label)
        {
            int count = 0;

            try
            {
                List<string> selectedTypes = new List<string>();
                foreach (GuidanceType type in GuidanceType.AllGuidanceTypes)
                {
                    if (!string.IsNullOrEmpty(type.NodeDisplayName) && string.Compare(type.NodeDisplayName, label) == 0)
                    {
                        selectedTypes.Add(type.Name);
                    }
                }

                //TODO: cache the result per guidance type.
                foreach (GuidanceItem item in this.Items.Values)
                {
                    GuidanceItemFileReference fileRef = item as GuidanceItemFileReference;
                    if (fileRef != null)
                    {
                        if (selectedTypes.Contains(fileRef.GuidanceType.Name))
                        {
                            count++;
                        }
                    }
                }
            }
            catch (Exception)
            {
                //do nothing
            }

            return count;
        }

        /// <summary>
        /// Find a view by it's capton in the library folder structure.
        /// </summary>
        /// <param name="viewCaption"></param>
        /// <returns></returns>
        public View FindViewByCaption(string viewCaption)
        {
            foreach (View view in Views.Values)
            {
                if (view.Caption == viewCaption)
                {
                    return view;
                }
            }

            return FindViewByCaptionInFolders(Folders, viewCaption);
        }

        /// <summary>
        /// Find a folder by it's capton in the library folder structure.
        /// </summary>
        /// <param name="folderCaption"></param>
        /// <returns></returns>
        public Folder FindFolderByCaption(string folderCaption)
        {
            return FindFolderByCaptionInFolders(Folders, folderCaption);
        }

        /// <summary>
        /// Recursively find the view in the folder collection.
        /// </summary>
        /// <param name="folderCollection"></param>
        /// <param name="viewCaption"></param>
        /// <returns></returns>
        private View FindViewByCaptionInFolders(FolderCollection folderCollection, string viewCaption)
        {
            foreach (Folder folder in folderCollection.Values)
            {
                foreach (View view in folder.Views.Values)
                {
                    if (view.Caption == viewCaption)
                    {
                        return view;
                    }
                }
                return FindViewByCaptionInFolders(folder.Folders, viewCaption);
            }
            return null;
        }

        /// <summary>
        /// Recursively find the folder in the folder collection.
        /// </summary>
        /// <param name="folderCollection"></param>
        /// <param name="folderCaption"></param>
        /// <returns></returns>
        private Folder FindFolderByCaptionInFolders(FolderCollection folderCollection, string folderCaption)
        {
            foreach (Folder folder in folderCollection.Values)
            {
                if (folder.Caption == folderCaption)
                {
                    return folder;
                }
                return FindFolderByCaptionInFolders(folder.Folders, folderCaption);
            }
            return null;
        }

        /// <summary>
        /// Return a list of guidance items of the given guidance type.
        /// </summary>
        /// <param name="guidanceType"></param>
        /// <returns></returns>
        public GuidanceItemFileReferenceCollection GetGuidanceItemFileReferenceForGuidanceType( GuidanceType guidanceType )
        {
            GuidanceItemFileReferenceCollection guidanceItems = GuidanceItemFileReferenceCollection.New();

            //Create the list to display
            foreach (GuidanceItem guidanceItem in Items.Values)
            {
                GuidanceItemFileReference gifr = guidanceItem as GuidanceItemFileReference;
                if (gifr != null && //If it is a file reference
                    (gifr.GuidanceType.Name == guidanceType.Name || //Its guidance type is the same
                    (!string.IsNullOrEmpty(guidanceType.NodeDisplayName) 
                        && string.Compare(gifr.GuidanceType.NodeDisplayName, guidanceType.NodeDisplayName) == 0)) && //Or it belong to the same container label
                    !guidanceItems.ContainsKey(gifr.Id)) //And the item has not been added before.
                {
                    guidanceItems.Add(gifr.Id, gifr);
                }
            }

            return guidanceItems;
        }
    }
}

